﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Xml.Linq;
using Microsoft.Win32;

namespace Test.AutoUpdater {
    public class Updater
    {
        private Timer _speedTimer;
        private DateTime _startTime;
        private DateTime _remainingTime;

        private double _progressValue;
        private double _receivedSize;
        private double? _totalSize;

        private string _statusDescription;
        private string _downLoadSpeed = "0";

        private bool _isDownloading;

        public UpdateInfo _updateInfo;

        private WebClient webClient;

        public Updater()
        {
        }

        public Updater(UpdateInfo updateInfo)
        {
            _updateInfo = updateInfo;
        }

        /// <summary>
        /// 下载速度
        /// </summary>
        public string DownLoadSpeed {
            get { return _downLoadSpeed; }
            set { _downLoadSpeed = value; }
        }

        /// <summary>
        /// 下载剩余时间
        /// </summary>
        public DateTime RemainingTime {
            get { return _remainingTime; }
            set { _remainingTime = value; }
        }

        /// <summary>
        /// 进度值
        /// </summary>
        public double ProgressValue {
            get { return _progressValue; }
            set { _progressValue = value; }
        }

        /// <summary>
        /// 已下载文件大小
        /// </summary>
        public double ReceivedSize {
            get { return _receivedSize; }
            set { _receivedSize = value; }
        }

        /// <summary>
        /// 文件大小
        /// </summary>
        public double? TotalSize {
            get { return _totalSize; }
            set { _totalSize = value; }
        }

        /// <summary>
        /// 当前状态
        /// </summary>
        public string StatusDescription {
            get { return _statusDescription; }
            set { _statusDescription = value; }
        }

        /// <summary>
        /// 下载模式
        /// </summary>
        public bool IsDownloading {
            get { return _isDownloading; }
            set { _isDownloading = value; }
        }

        public void UpdatePlatform()
        {
            webClient = new WebClient();
            webClient.DownloadDataCompleted += OnDownloadDataCompleted;
            checkInfoEvent += delegate(bool isUpdate,string info,RemoteFileInfo remoteFileInfo)
            {
                if (isUpdate)
                {
                    _updateInfo.TempPath = Utility.GetTempDirectory() + "//";
                    var filePath = _updateInfo.TempPath + _updateInfo.FileName;
                    StatusDescription = " 正在下载...";
                    _speedTimer = new Timer(SpeedTimerOnTick,null,0,1000);
                    _startTime = DateTime.Now;
                    DownLoadFile(_updateInfo.RemoteUrl+_updateInfo.FileName, filePath);                       
                }
                else
                {
                    Console.WriteLine(info);
                }
            };
            webClient.DownloadDataAsync(new Uri(_updateInfo.RemoteUrl+ "/update.xml"));
        }

        private void DownLoadFile(string remotePath, string localPath) {
            webClient.DownloadProgressChanged += OnDownloadProgressChanged;
            webClient.DownloadFileCompleted += OnDownloadFileCompleted;
            webClient.DownloadFileAsync(new Uri(remotePath), localPath);
        }

        private void OnDownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e) {
            var value = e.ProgressPercentage;
            if (value > 90) {
                value = 90;
            }
            ProgressValue = value;

            ReceivedSize = e.BytesReceived / (1024.0 * 1024.0);
            TotalSize = e.TotalBytesToReceive / (1024.0 * 1024.0);
            _updateInfo.ReceivedBytes = e.BytesReceived;
            _updateInfo.FileSize = e.TotalBytesToReceive;
        }

        private void OnDownloadFileCompleted(object sender, AsyncCompletedEventArgs e) {
            if (e.Cancelled) {
                StatusDescription = "更新被取消！";
                return;
            }

            if (e.Error is WebException) {
                StatusDescription = "请检测您的网络或防火墙设置！";
                return;
            }

            //下载完成
            _speedTimer.Dispose();
            _speedTimer = null;
            IsDownloading = false;

            //验证文件
            var filePath = _updateInfo.TempPath + _updateInfo.FileName;
            StatusDescription = "安全校验...";
            if (!VerifyFileMd5(filePath))
                return;
            Logs.Log.Info("更新完成！");

        }

        private class RemoteFileInfo
        {
            #region 远程包信息
            private string _remoteAppName;
            private Version _remoteVersion;
            private string _md5;
            private string _desc;

            /// <summary>
            /// 远程包名
            /// </summary>
            public string RemoteAppName {
                get { return _remoteAppName; }
                set { _remoteAppName = value; }
            }

            /// <summary>
            /// 远程包版本
            /// </summary>
            public Version RemoteVersion
            {
                get { return _remoteVersion; }
                set { _remoteVersion = value; }
            }

            /// <summary>
            /// 包MD5
            /// </summary>
            public string Md5
            {
                get { return _md5; }
                set { _md5 = value; }
            }

            /// <summary>
            /// 描述
            /// </summary>
            public string Desc
            {
                get { return _desc; }
                set { _desc = value; }
            }
        

            #endregion            
        }
        private RemoteFileInfo remoteFileInfo;

        private void OnDownloadDataCompleted(object sender, DownloadDataCompletedEventArgs e)
        {
            MemoryStream stream = new MemoryStream(e.Result);

            XDocument xDoc = XDocument.Load(stream);
            remoteFileInfo = new RemoteFileInfo();
            XElement root = xDoc.Element("UpdateInfo");
            remoteFileInfo.RemoteAppName = root.Element("RemoteAppName").Value;
            remoteFileInfo.RemoteVersion = root.Element("RemoteVersion") == null || string.IsNullOrEmpty(root.Element("RemoteVersion").Value) ? null : new Version(root.Element("RemoteVersion").Value);
            remoteFileInfo.Md5 = root.Element("Md5").Value;
            remoteFileInfo.Desc = root.Element("Desc").Value;
            stream.Close();
            CheckUpdate(remoteFileInfo);
        }

        private delegate void CheckInfoEvent(bool isUpdate,string info,RemoteFileInfo remoteFileInfo);
        private CheckInfoEvent checkInfoEvent;

        private void CheckUpdate(RemoteFileInfo remoteFileInfo)
        {
            string info = string.Empty;
            bool isUpdate = false;
            if (_updateInfo.CurrentVersion >= remoteFileInfo.RemoteVersion)
            {
                info = "当前已是最新版本！";
                isUpdate = false;
            }
            else
            {
                info = "当前版本过低，需要更新！";
                isUpdate = true;
            }
            if (checkInfoEvent!=null)
            {
                checkInfoEvent(isUpdate,info,remoteFileInfo);
            }
        }

        private bool VerifyFileMd5(string fileName) {
            Thread.Sleep(1000);
            var md5 = Utility.GetFileMD5(fileName);
            if (!_updateInfo.FileMd5.Equals(md5)) {
                StatusDescription = "更新失败，更新文件MD5码不一致！";
                Logs.Log.Info("Update file MD5 inconsistent. ");
                Directory.Delete(_updateInfo.TempPath, true);
                return false;
            }

            return true;
        }

        private void SpeedTimerOnTick(object sender) {
            var interval = DateTime.Now - _startTime;

            //下载速度
            DownLoadSpeed = interval.Seconds < 1
                ? Utility.ConvertToUnit(_updateInfo.ReceivedBytes)
                : Utility.ConvertToUnit(_updateInfo.ReceivedBytes / interval.Seconds);
            //剩余时间
            var size = (_updateInfo.FileSize - _updateInfo.ReceivedBytes) / (1024 * 1024);
            _remainingTime = new DateTime().AddSeconds(Convert.ToDouble(size));

            Console.WriteLine(string.Format("下载速度：{0}   剩余时间：{1}",DownLoadSpeed,size));
        }
    }
}
